
import serial
import os
import sys
import tomllib
import time
from colorama import init, Cursor, Fore, Back
import threading
import queue


init(autoreset=True)

port_path = "/dev/ttyS0"
baud_rate = int(115200)
serial_handler = None
messageQueue = queue.Queue()


def config():
    global serial_handler
    """
    Load config from config.toml
    """
    if os.path.exists("./config.toml"):
        with open("./config.toml","rt") as cfg_file:
            
            ws = tomllib.loads(cfg_file.read()) # type: ignore

            if "port_path" in ws and "baud_rate" in ws:
                sys.stdout.write("Successfuly load saved config\n")

                port_path = ws["port_path"]
                baud_rate = ws["baud_rate"]
    else:
        sys.stdout.write("Using default config\n")
        

    """
    Get Serial Handler
    """
    try:
        serial_handler = serial.Serial(port_path, baud_rate)

    except:

        print("fatal: Fail to open serial.")
        
        time.sleep(0.5)
        
        exit(-1)

        return;


"""
Update Status Bar
"""
def update_stray_msg(s):
    sys.stdout.write(Cursor.POS(0,0)
                        +Fore.RESET
                        +Back.RESET
                        +"[STATUS]: {}.".format(s).ljust(28))


"""
Draw
"""
def drawer(event):
    print("initiate drawer")
    while True:
        if(event.is_set()):
            exit(0)
        messages :dict = messageQueue.get()


        def toint(b):
            return int.from_bytes(b[0])

        messages = dict(sorted(messages.items(),key=toint))

    


        update_stray_msg("Listening")

        top_line = 2;
        sys.stdout.write(str(Cursor.POS(0,top_line)))

        colt = ["ID","MESSAGE","TIMESTAMP"]
        colw = [6   , 20      , 18        ]

        sys.stdout.write(Back.YELLOW
                            +'|'.join(colt[i].center(colw[i]) 
                                    for i in range(0,len(colt))))

        
        for idx, msg_id in enumerate(messages):
            
            
            sys.stdout.write(Cursor.DOWN(1)+Cursor.POS(0,top_line+idx+1))
            
            if(msg_id.hex()[0:2] == '02' or '02'):
                cols = [
                    'm'+msg_id.hex(), 
                    "p:"+str(int.from_bytes(messages[msg_id][0][0:2])).ljust(4)
                        +' | v:'
                        +str(int.from_bytes(messages[msg_id][0][2:4],signed=True)).ljust(4),
                    str(messages[msg_id][1])
                        ] 
            else:
                cols = [msg_id.hex() , messages[msg_id][0].hex() , str(messages[msg_id][1]) ] 
            
            colc = [Fore.YELLOW, Fore.RED, Fore.BLUE]

            fmt_o =  []
            for i in range(0,len(cols)):
                fmt_o.append(colc[i] + cols[i].center(colw[i]))
            
            color = Back.BLACK
            if(idx % 2 == 0):
                color = Back.CYAN

            sys.stdout.write((Fore.RESET+"|").join(fmt_o))
            


"""
Receiver
"""
def receiver(event,serial_handler):
    
    def receive():
        aligned = False # helper


        sys.stdout.write("\033[?25l")

        messages = dict() # message register

        os.system("clear")

        update_stray_msg("Start listening")


        while True:
            if(event.is_set()):
                exit(0)
            try: 
                frame : bytes = serial_handler.read(20) # type: ignore
            

                if(frame[:9] == b'\xff\xff\xff\xff\xff\xff\xff\xff\xff'):
                    aligned = True
                else:
                    aligned = False
                    serial_handler.read(1)

                if(frame == b'' or not aligned):
                    continue


                frame = frame[9:]
                messages[bytes(frame[1:3])] = bytes(frame[3:10]), time.time()

                messageQueue.put(messages)

            except serial.SerialException:

                update_stray_msg("Disconected")

                time.sleep(0.5)


    receive()


def main():

    event = threading.Event()
    config()
    a = threading.Thread(target=drawer,args=[event])
    b = threading.Thread(target=receiver,args=[event,serial_handler])
    
    a.start()
    b.start()

    while(a.is_alive() and b.is_alive()):
        time.sleep(0.5)

    event.set()
    

try:

    main()

except KeyboardInterrupt:

    sys.stdout.write("\033[?25h"
                     +Fore.RESET
                     +Back.RESET)
    exit()